/**
 * Copyright [2019] [LiBo/Alex of copyright liboware@gmail.com ]
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.dess.base.aspectj;

import com.hyts.dess.base.aspectj.logger.LogAdvisor;
import com.hyts.dess.base.aspectj.logger.LogPrinter;
import com.hyts.dess.base.aspectj.logger.LogTest;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.matcher.ElementMatchers;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.reflections.scanners.FieldAnnotationsScanner;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ConfigurationBuilder;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * @project-name:dess
 * @package-name:com.hyts.dess.base.aspectj
 * @author:LiBo/Alex
 * @create-date:2020-07-18 15:21
 * @copyright:libo-alex4java
 * @email:liboware@gmail.com
 * @description:
 */
public class AnnotationHandler {

    /**
     * 默认注释的key和value的容器对象
     */
    private static final Map<Class,Object> DEFAULT_ANNOTATION_KEY_AND_VALUE_CONTAINER = new HashMap<>();

    /**
     * byteBuddy 对象操作处理功能实现
     */
    private static ByteBuddy byteBuddy = new ByteBuddy();

    /**
     * 初始化操作功能实现
     */
    public static Map<Class,Object> init(String basePackages,Class adviceClass,Class... clazz) throws InstantiationException, IllegalAccessException {
        Reflections reflections = new Reflections(new ConfigurationBuilder().forPackages(basePackages).
                addScanners(new SubTypesScanner()).addScanners(new MethodAnnotationsScanner()));
        // 判断校验传入的clazz对象数组是否为空!
        if(Objects.nonNull(clazz) && ArrayUtils.isNotEmpty(clazz)){
            for(Class clazzElement: clazz){
               Set<Class> classSet = reflections.getTypesAnnotatedWith(clazzElement);
               if(CollectionUtils.isNotEmpty(classSet)){
                   for(Class clazzSuject : classSet){
                       DEFAULT_ANNOTATION_KEY_AND_VALUE_CONTAINER.put(clazzSuject,enhance(clazzSuject,adviceClass));
                   }
               }
            }
        }
        return DEFAULT_ANNOTATION_KEY_AND_VALUE_CONTAINER;
    }

    /**
     * 增强操作对象功能实现操作服务处理
     * @param targetClass
     * @return
     */
    public static Object enhance(Class targetClass,Class adviceClass) throws IllegalAccessException, InstantiationException {
        return byteBuddy.subclass(targetClass).method(ElementMatchers.any())
                .intercept(Advice.to(adviceClass))
                .make()
                .load(targetClass.getClassLoader())
                .getLoaded()
                .newInstance();
    }

    /**
     * 获取相关的容器对象集合和相关映射关系
     * @return
     */
    public static Map<Class, Object> getDefaultAnnotationKeyAndValueContainer() {
        return DEFAULT_ANNOTATION_KEY_AND_VALUE_CONTAINER;
    }

    /**
     * 获取相关的bean对象操作处理
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz){
        return (T) getDefaultAnnotationKeyAndValueContainer().get(clazz);
    }




    public static void main(String[] args){
        try {
            AnnotationHandler.init("com.hyts", LogAdvisor.class, LogPrinter.class);
            getBean(LogTest.class).bar(1);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
